package util

import (
	"math/rand"
	"net/http"
	"strings"
	"time"

	"strconv"

	"errors"

	"gitee.com/sheldwu/httpdns/config"
	"github.com/wangtuanjie/ip17mon"
)

const (
	GNet = iota
	Wifi
)

type nodeWithWeight struct {
	node    string
	weight  int
	start   int
	end     int
	choosen bool
}

type nodeSlice []nodeWithWeight

func (ns nodeSlice) Swap(i, j int) {
	ns[i], ns[j] = ns[j], ns[i]
}

func (ns nodeSlice) Len() int {
	return len(ns)
}

func (ns nodeSlice) Less(i, j int) bool {
	return ns[i].weight < ns[j].weight
}

func GetIdcByIp(ip string) (idc string, province string) {
	locate, err := ip17mon.Find(ip)
	if err != nil {
		return config.DefaultIdc, ""
	}
	return getIdcByProvince(locate.Region), locate.Region
}

func GetIdcByIccid(iccid string) (idc string, province string, provider string) {
	var provinceCode string
	var ok bool
	switch {
	//移动
	case strings.HasPrefix(iccid, "898600") || strings.HasPrefix(iccid, "898602"):
		provider = "移动"
		provinceCode = iccid[8:10]
		province, ok = config.ProviderProvinceCode["mobile"][provinceCode]
		if !ok {
			return config.DefaultIdc, province, provider
		}
	//联通
	case strings.HasPrefix(iccid, "898601") || strings.HasPrefix(iccid, "898609"):
		provider = "联通"
		provinceCode = iccid[9:11]
		province, ok = config.ProviderProvinceCode["unicom"][provinceCode]
		if !ok {
			return config.DefaultIdc, province, provider
		}

	//电信，目前没有电信卡，并且电信卡用的是市区号。
	case strings.HasPrefix(iccid, "898603") || strings.HasPrefix(iccid, "898606"):
		provider = "电信"
		return config.DefaultIdc, province, provider
	}

	return getIdcByProvince(province), province, provider
}

func getIdcByProvince(province string) (idc string) {
	RwDovelock.RLock()
	idc, ok := config.Province2Idc[province]
	RwDovelock.RUnlock()

	if !ok {
		idc = config.DefaultIdc
	}

	return idc
}

func GetIp(req *http.Request) (ip string) {
	ips, ok := req.Form["hijacked_ip"]
	if !ok {
		s := strings.Split(req.RemoteAddr, ":")
		ip = s[0]
	} else {
		ip = ips[0]
	}
	return
}

func IsdebugMode(req *http.Request) bool {
	_, ok := req.Form["debug"]
	return ok
}

func GetNet(req *http.Request) (int, error) {
	nets, ok := req.Form["net"]
	if !ok {
		return 0, errors.New("lost param:net")
	}

	net := nets[0]

	//G网
	if net == "2G" || net == "3G" || net == "4G" {
		return GNet, nil
	}
	//wifi
	if net == "wifi" {
		return Wifi, nil
	}
	return 0, errors.New("can not parse params net, aviable values are: 2G,3G,4G,wifi")
}

func GetIccid(req *http.Request) (string, error) {
	iccids, ok := req.Form["iccid"]
	if !ok {
		return "", errors.New("lost param:iccid")
	}

	iccid := iccids[0]

	if len(iccid) != 20 {
		return "", errors.New("iccid length must be 20")
	}

	return iccid, nil
}

func SortByWeight(lists []string) []string {
	nodes, endPoint := getStandardLists(lists)

	rand.Seed(time.Now().UnixNano())

	n := 0
	if endPoint != 0 {
		n = rand.Intn(endPoint)
	}

	var firstNode string
	for pos, item := range nodes {
		if n >= item.start && n <= item.end {
			nodes[pos].choosen = true
			firstNode = item.node
		}
	}

	newList := make([]string, 0)
	if firstNode != "" {
		newList = append(newList, firstNode)
	}

	for _, item := range nodes {
		if !item.choosen {
			newList = append(newList, item.node)
		}
	}

	return newList
}

// endpoint means list length
func getStandardLists(lists []string) (nodes nodeSlice, endPoint int) {
	nodes = make(nodeSlice, 0)
	startPoint := 0

	for _, list := range lists {
		pieces := strings.Split(list, ":")
		weight := 1
		// 默认权重为1
		if len(pieces) > 2 {
			weight, _ = strconv.Atoi(pieces[2])
		}
		endPoint = startPoint + weight - 1
		node := nodeWithWeight{pieces[0] + ":" + pieces[1], weight, startPoint, endPoint, false}
		nodes = append(nodes, node)
		startPoint = endPoint + 1
	}
	return nodes, endPoint
}

func SortMqttList(priorityIdc string) []string {
	list := SortByWeight(config.Idc2Mqtt[priorityIdc])

	for idc, thisList := range config.Idc2Mqtt {
		if idc == priorityIdc {
			continue
		}

		list = append(list, SortByWeight(thisList)...)
	}
	return list
}
